/* 
   Title:   "NoDelta" formalism.
   Version: 1.04e-33 (Sep 13 2015).
   Author:  R. J. Cano (e-mail: reemmmyyyyccccaaanno at GMail dot com).

                                               ...To Russ Cox,
                                                       David Applegate,
                                                            and Joerg Arndt.
*/

#include <stdio.h>
#include <stdlib.h>

enum outputFormats {OEIS};

/* Settings */
const unsigned int Stirling2ndKind=   1;
const unsigned int display=        OEIS;

typedef unsigned long zword; /*  Important: Under the suspect you are getting wrong results
                                            (i.e. negative terms), please use a bigger unsigned
                                            datatype to ensure the problem is not due to overflows.
                              */

typedef struct Term {
  zword b, c;
  unsigned short int k;
  unsigned short int m;
  struct Term *l;
  struct Term *i;
  struct Term *r;
} Term;

Term* getAnUnit(void);
int isJustAnUnit(Term*);
Term* getThisTerm(zword,zword,zword,zword,Term*,Term*,Term*);
void prettyPrint(Term*,int);
void listPut(Term**,Term*);
Term* clone(Term*);
void discard(Term**);
void simplify(Term**);
Term* SymbolicProduct(Term*,Term*);
void parse(Term*,Term**);

int main(int argc, char *argv[]) {
  Term *t;
  Term *q0;
  Term *q;
  int N;
  int j;
  char* queryText[2]={"exponent","last row"};

  if (argc==2) {
    N= strtol(argv[1], NULL, 10);
  }
  else do {
    printf("\n\t(Current precision: %lu Bits) Please specify the %s: (N>0)",8*sizeof(zword),queryText[Stirling2ndKind]);
    scanf("%i",&N);
  } while (N<=0);

  q0=getAnUnit();
  q=NULL;
  for (j=1;j<=N;j++) {
    t=q0;
    parse(q0,&q);
    q0=q;
    q=NULL;
    if (Stirling2ndKind) {
      prettyPrint(q0,OEIS);
      if (j<N) printf(",");
    }  
    discard(&t);
  }
  if (!Stirling2ndKind) prettyPrint(q0,OEIS);
  discard(&q0);
  return EXIT_SUCCESS;
}

Term* getAnUnit(void) {
  Term *answer=(Term*)malloc(sizeof(Term));
  answer->c=1;
  answer->k=0;
  answer->m=0;
  answer->b=0;
  answer->l=NULL;
  answer->i=NULL;
  answer->r=NULL;
  return answer;
}

int isJustAnUnit(Term* thisOne) {
  if (thisOne!=NULL) {
    if(    (thisOne->c==1)
        && (thisOne->k==0)
        && (thisOne->m==0)
        && (thisOne->b==0)
        && (thisOne->l==NULL)
        && (thisOne->i==NULL)
        && (thisOne->r==NULL)
    ) return 1;     
  }
  return 0;  
}

Term* getThisTerm(zword cc, zword kk, zword mm, zword bb, Term* ll, Term* ii, Term* rr) {
  Term *answer=(Term*)malloc(sizeof(Term));
  answer->c=cc;
  answer->k=kk;
  answer->m=mm;
  answer->b=bb;
  answer->l=ll;
  answer->i=ii;
  answer->r=rr;
  return answer;  
}

void prettyPrint(Term* t0, int format) {
  Term *t;
  zword j;
  zword s;
  zword k;
  zword l;
  char* symb[9]={"1","\\delta_{","\\cancel{\\delta}_{","}","\\left(","\\right)","byBits(",")","+"};
  if (t0 != NULL) {
    switch (format) {
      case OEIS:
        if (Stirling2ndKind) {
          while (t0!=NULL) {
            s=0;
            t=(t0->i);
            while (t!=NULL) {
              s+=t->c;
              t=(t->r);
            }
            printf("%ld",s);
            t0=(t0->r);
            if (t0!=NULL) printf("%c",',');
          }      
        } else {
          while (t0!=NULL) {
            switch (t0->k) {
              case 0:
              case 1:
              case 2:
                printf("%s",symb[t0->k]);
                break;
              default:
                printf("%s","Error");
            }
            switch (t0->m) {
              case 0:
                j=1;
                while (j<=t0->b) {
                  printf("%ld",j);
                  if (j<t0->b) printf("%c",',');
                  j++;
                }
                break;
              case 1:
                printf("%s%ld%s",symb[6],t0->b,symb[7]);
                break;
              default:
                printf("%s","Error");
            }
            printf("%s",symb[3]);
            t=t0->i;
            if (t!=NULL) {
              if (!isJustAnUnit(t)) {
                printf(symb[4]);
                while (t!=NULL) {
                  if (t->c>1) printf("%ld",t->c);
                  switch (t->k) {
                    case 1:
                    case 2:
                      printf("%s",symb[t->k]);
                      break;
                    default:
                      printf("%s","Error");
                  }
                  for(j=0,s=1;s<(t->b);j++)
                    s+=1<<j;
                  for(k=0;k<=j;k++) {
                    l=(1<<k);
                    if (l&(t->b)) {
                      printf("%ld",k+1);
                      if (k+1<j) printf("%c",',');
                    }
                  }
                  printf("%s",symb[3]);                  
                  t=t->r;
                  if (t!=NULL) printf("%s",symb[8]);                  
                }
                printf(symb[5]);
              }
            }
            t0=t0->r;
            if (t0!=NULL) printf("%s",symb[8]);
          }         
        }
      break;
    }
  }
}

void listPut(Term** toThisList, Term* newInput) {
  Term* t;
  if ((*toThisList) == NULL) {
    (*toThisList)=newInput;
    (*toThisList)->l=NULL;
    (*toThisList)->r=NULL;
  } else {
    t=(*toThisList);
    while ((t->r) != NULL) t=t->r;
    (t->r)=newInput;
    newInput->l=t;
    newInput->r=NULL;
  }
}

Term* clone(Term* t) {
  Term *z, *answer_part2;
  Term *answer_part1=(Term*)malloc(sizeof(Term));
  answer_part1->c=t->c;
  answer_part1->k=t->k;
  answer_part1->m=t->m;
  answer_part1->b=t->b;
  answer_part1->l=NULL;
  answer_part1->i=NULL;
  answer_part1->r=NULL;
  if (t->i != NULL) {
    answer_part2=NULL;
    t=t->i;
    while (t != NULL)  {
      z=clone(t);
      listPut(&answer_part2,z);
      t=t->r;
    }
    answer_part1->i=answer_part2;
  }
  return answer_part1;
}

void discard(Term** data) {
  Term *t0, *t;
  if ((*data) != NULL) {
    while ((*data)->l != NULL) (*data)=(*data)->l;
    while ((*data) != NULL)  {
      t=(*data)->i;
      (*data)->i=NULL;
      if (t != NULL) {
        while (t->l != NULL)  t=t->l;
        while (t != NULL)  {
          t0=t;
          t=t->r;
          if (t != NULL) t->l=NULL;
          free(t0);
        }
      }
      t=NULL;
      t0=(*data);
      (*data)=(*data)->r;
      if ((*data) != NULL) (*data)->l=NULL;
      free(t0);
    }
  }
  (*data)=NULL;
}

void simplify(Term** Operand) {
  zword c;
  Term *L,*R,*x,*y,*t,*u,*w,*q;
  c=0;
  if ((*Operand) != NULL) {
    c++;
    R=(*Operand);
    while (R != NULL)  {
      c++;
      R=R->r;
    }
    if (c>3) {
      L=(*Operand)->r;
      while (L->r != NULL)  {
        R=L->r;
        if ( (L->c == R->c) && (L->k == R->k) && (L->m == R->m) ) {
          x=L->i;
          y=R->i;
          L->i=NULL;
          R->i=NULL;
          t=L->l;
          t->r=R;
          R->l=t;
          free(L);
          t=x;
          while (t->r != NULL) t=t->r;
          t->r=y;
          y->l=t;
          q=x;
          x=NULL;
          u=q;
          while (u != NULL)  {
            if (u->r != NULL) {
              w=u->r;
              while (w != NULL)  {
                if ( (w->k==u->k) && (w->m==u->m) && (w->b==u->b) ) {
                  w->c+= u->c;
                  u->c= 0;
                }
                w=w->r;
              }
            }
            u=u->r;
          }
          u=q;
          while (u != NULL)  {
            if (u->c > 0) {
              w=clone(u);
              listPut(&x,w);
            }
            u=u->r;
          }
          u=NULL;
          w=NULL;
          discard(&q);
          R->i=x;
          L=R;
        }
        L=L->r;
      }
    }
  }
}

Term* SymbolicProduct(Term* A, Term* B) {
  Term *answer,*x,*y,*t;
  if ( (A != NULL) && (B != NULL) ) {
    answer=NULL;
    if ( isJustAnUnit(A) ) {
      x=B;
      while (x != NULL)  {
        t=clone(x);
        listPut(&answer,t);
        x=x->r;
      }
    }
    else if ( isJustAnUnit(B) ) {
      y=A;
      while (y != NULL)  {
        t=clone(y);
        listPut(&answer,t);
        y=y->r;
      }
    }
    else {
      x=A;
      while (x != NULL)  {
        y=B;
        while (y != NULL)  {
          t=getThisTerm((x->c)*(y->c),x->k,x->m,(x->b)|(y->b),NULL,NULL,NULL);
          listPut(&answer,t);
          y=y->r;
        }
        x=x->r;
      }
    }
  }
  else {
    answer=getAnUnit();
    answer->c=0;
  }
  return answer;
}

void parse(Term* myInput, Term** myOutput) {

  Term *x,*y,*z,*w,*t;
  int i;

  zword alpha, N=0;
  x=myInput;
  if (x != NULL) {
    if (isJustAnUnit(x)) {
      t=getAnUnit();
      w=getThisTerm(1,1,0,1,NULL,t,NULL);
      listPut(myOutput,w);
    } else { while (x != NULL)  {
        if (x->k==1) {
          if (x->m==0) {
            N=x->b+1;
            t=getAnUnit();
            w=getThisTerm(1,1,0,N,NULL,t,NULL);
            listPut(myOutput,w);
            alpha=-2;
            for(i=0;i<N;i++) alpha+=(1<<i);
            if (N==2) t=getAnUnit(); else t=getThisTerm(1,1,1,alpha,NULL,NULL,NULL);
            w=getThisTerm(1,2,0,2,NULL,t,NULL);
            listPut(myOutput,w);
          }
        }
        if (x->k==2) {
          if (x->m==0) {
            w=NULL;
            i=1;
            while (i <= x->b)  {
              t=getThisTerm(1,1,1,(zword)((1<<(i-1))+(1<<(N-1))),NULL,NULL,NULL);
              listPut(&w,t);
              i++;
            }
            y=clone(x);
            t=y->i;
            y->i=NULL;
            z=SymbolicProduct(w,t);
            y->i=z;
            z=NULL;
            discard(&w);
            discard(&t);
            listPut(myOutput,y);
            if (x->b+1==N) {
              w=getThisTerm(1,2,0,N,NULL,getAnUnit(),NULL);
              listPut(myOutput,w);
            } else {
              y=clone(x);
              (y->b)++;
              z=y->i;
              while (z != NULL)  {
                z->b+=(1<<(N-1))-(1<<(y->b-1));
                z=z->r;
              }
              listPut(myOutput,y);
            }
          }
        }
        x=x->r;
      }
    }
    simplify(myOutput);
  }
}

/* Note(s):

  By using the present version of this code, and for the same N, the result shown after its execution
  is identical to calling and printing the result for the following PARI-GP definition:

  v(n)= {my(a=vector(n*(n+1)/2),b);for(j=1,n,b=j*(j-1)/2;for(k=1,j,a[b+k]=stirling(j,k,2))); a}

  Extra functionality was added: By setting to zero the "Stirling2ndKind" flag, this program will
  generate amsmath/amssymb LaTEX source, for the product-of-summations method alternative to the
  multinomial theorem, on which this code is based upon.

   1) On the Speed and performance:
  =================================

  Surely those readers experienced in languages similar to C (with pointers) could identify in the code
  what looks like a design flaw. The doubly linked lists are managed using only the heads. If the list tail
  is missing, some cycles will be wasted each time listPut() is called, since the list must be traversed
  starting from the head in order to find the tail. For small values of N this is not a problem, however
  the code wont be the fastest possible.

  This feature has to do with the same design originally implemented in Pascal (and compiled using the
  tools publicly available at http://www.freepascal.org although the same code would work with Borland
  Turbo Pascal 5.5) from where the program was translated with some upgrades, being emphasized as the main
  goal not to be the fastest but to verify instead the conceptual basis on which this code is based upon. 

  For example a possible upgrade could be also to replace the simplification routine by implementing some
  available algorithm faster than O(N^2);

   2) Additional Limitation:
  ===========================

  Due the fixed size in bits of every data type implemented by each environment (Usually either 32 or 64 Bits),
  this program will fail from some N onwards. Unless you re-define "zword" with a bigger unsigned type supported
  by your compiler, reliable results will be obtained only with this version up to N=25 on 64 Bits machines and up
  to N=15 on 32 Bits machines.
  
*/